Inside Macintosh: Memory

Previous | Chapter Top | Chapter Contents | Next

Using the System Heap

The system heap is used to store most of the information needed by the Operating System and other system software components. As a result, it is ideal for storing information needed by a system extension (which by definition extends the capabilities of system software). You might also need to use the system heap to store a task record and the code for an interrupt task that should continue to be executed when your application is not the current application.

Allocating blocks in the system heap is straightforward. Most ordinary Memory Manager routines have counterparts that allocate memory in the system heap zone instead of the current heap zone. For example, the counterpart of the NewPtr function is the NewPtrSys function. The following line of code allocates a new nonrelocatable block of memory in the system heap to store a Time Manager task record:

myTaskPtr := QElemPtr(NewPtrSys(SizeOf(TMTask)));

Alternatively, you can change the current zone and use ordinary Memory Manager operations, as follows:

SetZone(SystemZone);
myTaskPtr := QElemPtr(NewPtr(SizeOf(TMTask)));
...
SetZone(ApplicationZone);

You might also need to store the interrupt code itself in the system heap. For example, when an application that installed a vertical retrace task with the VInstall function is in the background, the Vertical Retrace Manager executes the task only if the vblAddr field of the task record points to a routine in the system heap.

Unfortunately, manually copying a routine into the system heap is difficult in Pascal. The easiest way to install code into the system heap is to place the code into a separate stand-alone code resource in your application's resource fork. You should set the system heap bit and the locked bit of the code resource's attributes. Then, when you need to use the code, you must load the resource from the resource file and cast the resource handle's master pointer into a procedure pointer (a variable of type ProcPtr ), as follows:

myProcHandle := GetResource(kProcType, kProcID);
IF myProcHandle <> NIL THEN
    myTaskPtr^.vblAddr := ProcPtr(myProcHandle^);

Because the resource is locked in memory, you don't have to worry about creating a dangling pointer when you dereference a handle to the resource. If you want the code to remain in the system heap after the user quits your application, you can call the Resource Manager procedure DetachResource so that closing your application's resource fork does not destroy the resource data. Note, however, that if you do so and your application crashes, the code still remains in the system heap.

Once you have loaded a code resource into memory and created a ProcPtr that references the entry point of the code resource, you can use that ProcPtr just as you can use any such variable. For example, you could assign the value of the variable to the vblAddr field of a vertical retrace task record (as shown just above). If you are programming in assembly language, you can then call the code directly. To call the routine from a high-level language such as Pascal, you'll need to use some inline assembly-language code. Listing 4 defines a routine that you can use to execute a procedure by address.

Listing 4 Calling a procedure by address

PROCEDURE CallByAddress (aRoutine: ProcPtr);
    INLINE      $205F,                  {MOVE.L (SP)+,A0}
                $4ED0;                  {JMP (A0)}

© 1997 Apple Computer, Inc.

Previous | Chapter Top | Chapter Contents | Next